# -*- coding:utf-8 -*-
# @FileName  :t5_frida_so.py
# @Time      :2021/10/7 12:24
import time

import frida, sys


# 坑1：中文包名报错,换成 12.9.1版本frida解决
# 坑2：在坑1中12.9.1版本是有英文包了，但是在js加载中Java.perform会报错  Error: Unexpected end of block  换成12.11.18版本的frida
# 坑3：字符数组转换出错，hookso文件会导致app强行停止
def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)


# hook so文件
jscode_so = '''
// Module模块中通过名字来返回内存地址的方法
var base_address = Module.findBaseAddress('libc.so')
send('base_address：' + base_address)

// Module模块中通过名字和函数名字来返回该函数内存地址的方法
var mod_address = Module.findExportByName('libc.so', 'dlopen')
send('mod_address：' + mod_address)

// Process模块中通过内存地址来返回该地址的的Module对象方法
var lib_module = Process.findModuleByAddress(base_address)
send('lib_module：' + lib_module.name)

// Interceptor监听模块，attach用内存地址和回调函数，该回调函数：
// onEnter可在函数执行开始执行
// onLeave可在函数执行完毕前执行
Interceptor.attach(mod_address, {
    onEnter: function (args) {
        //Memory模块 将内存地址通过其符合的编码变成字符串
        send('open[' + Memory.readUtf8String(args[0]) + ',' + args[1] + ']')
        //send('open[' + args[0] + ',' + args[1] + ']')
    },
    onLeave: function (retval) {
        send('retval:' + retval)
    }
})
'''
# hook 启动时执行的方法
jscode_main = '''
Java.perform(
    function (){
        var tqwee=Java.use('com.yaotong.crackme.MainActivity');
        tqwee.onCreate.overload('android.os.Bundle').implementation = function (v) {
            send('okk');
            this.onCreate(v)
        }
    }
)
'''
# hook 加密的重载方法 md5
jscode_md5 = '''
//打印堆栈信息
function printstack() {
    send(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
}

//字节数组转字符   有问题
function array2string(array) {
    var buffer = Java.array('byte', array)
    var res = ''
    for (var i = 0; i < buffer.length; ++i) {
        res += (String.fromCharCode(buffer[i]))
    }
    return res
}

//MD5的四种重载方法的hook
Java.perform(
    function () {
        var MessageDigest = Java.use('java.security.MessageDigest');
        MessageDigest.update.overload('[B').implementation = function (bytesarray) {
            send('第1种重载方法')
            //send(bytesarray)
            //send('ori：'+array2string(bytesarray));
            printstack()
            this.update(bytesarray)
        },
            MessageDigest.update.overload('byte').implementation = function (bytesarray) {
                send('第2种重载方法')
                //send('ori：'+array2string(bytesarray));
                this.update(bytesarray)
            },
            MessageDigest.update.overload('java.nio.ByteBuffer').implementation = function (bytesarray) {
                send('第3种重载方法')
                //send('ori：'+array2string(bytesarray));
                this.update(bytesarray)
            },
            MessageDigest.update.overload('[B', 'int', 'int').implementation = function (bytesarray) {
                send('第4种重载方法')
                //send('ori：'+array2string(bytesarray));
                this.update(bytesarray)
            },
            // hook   MessageDigest.getInstance的参数，也就是什么加密方法
            MessageDigest.getInstance.overloads[0].implementation = function (algorithm) {
                send("call --->getInstance for " + algorithm);
                return this.getInstance.overloads[0].apply(this, arguments)
            }
    }
)

'''

device = frida.get_usb_device()
pid = device.spawn(['com.iCitySuzhou.suzhou001'])
porecess = device.attach(pid)
script = porecess.create_script(jscode_md5)
script.on('message', on_message)
print('[*] 开始hook')
script.load()
device.resume(pid)
sys.stdin.read()
